#include "stdafx.h"
#include "Time.h"
#include <boost/lexical_cast.hpp>
#include "TypeDefination.h"

#define PARSE_FAILED(obj, ret) \
	{	\
		memset(obj, 0, sizeof(Time));	\
		return ret;	\
	}

namespace serika
{
	namespace ut
	{
		Time Time::INVALID;
	
		Time::Time()
		{
			memset(this, 0, sizeof(Time));
		}
	
	#ifdef WIN32
		Time::Time(__time32_t time, bool local)
		{
			if (local)
			{
				_localtime32_s(this, &time);
			}
			else
			{
				_gmtime32_s(this, &time);
			}
		}
	
		Time::Time(__time64_t time, bool local)
		{
			if (local)
			{
				_localtime64_s(this, &time);
			}
			else
			{
				_gmtime64_s(this, &time);
			}
		}
	#else
		Time::Time(time_t time, bool local)
		{
			if (local)
			{
				*this = *localtime(&time);
			}
			else
			{
				*this = *gmtime(&time);
			}
		}
	#endif
	
		Time::Time(const Time& other)
		{
			tm_sec = other.tm_sec;
			tm_min = other.tm_min;
			tm_hour = other.tm_hour;
			tm_mday = other.tm_mday;
			tm_mon = other.tm_mon;
			tm_year = other.tm_year;
			tm_wday = other.tm_wday;
			tm_yday = other.tm_yday;
			tm_isdst = other.tm_isdst;
		}
	
		Time::Time(const tm& other)
		{
			tm_sec = other.tm_sec;
			tm_min = other.tm_min;
			tm_hour = other.tm_hour;
			tm_mday = other.tm_mday;
			tm_mon = other.tm_mon;
			tm_year = other.tm_year;
			tm_wday = other.tm_wday;
			tm_yday = other.tm_yday;
			tm_isdst = other.tm_isdst;
		}
	
		const Time& Time::Now(bool local)
		{
			static Time t;
			time_t tt = time(0);
			if (local)
			{
	#ifdef WIN32
				localtime_s(&t, &tt);
	#else
				t = *localtime(&tt);
	#endif
			}
			else
			{
	#ifdef WIN32
				gmtime_s(&t, &tt);
	#else
				t = *gmtime(&tt);
	#endif
			}
			return t;
		}
	
		time_t Time::ToTimeT() const
		{
			return mktime(const_cast<Time*>(this));
		}
	
		const Time& Time::FromString(const Char* datetime, const Char* format /* = */ )
		{
			static Time t;
			if (t.Parse(datetime, format))
			{
				return t;
			}
			else
			{
				return INVALID;
			}
		}
	
		const Time& Time::FromDateString(const Char* date, const Char* format /* = */ )
		{
			static Time t;
			if (t.ParseDate(date, format))
			{
				return t;
			}
			else
			{
				return INVALID;
			}
		}
	
		const Time& Time::FromTimeString(const Char* time, const Char* format /* = */ )
		{
			static Time t;
			if (t.ParseTime(time, format))
			{
				return t;
			}
			else
			{
				return INVALID;
			}
		}
	
		String Time::ToString(const Char* format) const
		{
			const int bufsize = 1024;
			Char buf[bufsize];
#ifdef _UNICODE
			size_t len = wcsftime(buf, bufsize, format, this);
#else
			size_t len = strftime(buf, bufsize, format, this);
#endif
			if (len == 0)
			{
				return _T("");
			}
			else
			{
				return String(buf, len);
			}
		}
	
		bool Time::Parse(const Char* datetime, const Char* format)
		{
			const Char* pd = datetime;
			const Char* pf = format;
#ifdef _UNICODE
			size_t sd = wcslen(datetime);
			size_t sf = wcslen(format);
#else
			size_t sd = strlen(datetime);
			size_t sf = strlen(format);
#endif
			int state = 0;
			memset(this, 0, sizeof(Time));
	
			while (sd > 0 && sf > 0)
			{
				if (state == 0)
				{
					if (*pf == '%')
					{
						state = 1;
					}
					else
					{
						if (*pf != *pd++)
							PARSE_FAILED(this, false);
						--sd;
					}
				}
				else
				{
					int r = 0;
					switch (*pf)
					{
					case 'Y':
						if ((r = this->ParseNumber(pd, sd, 4)) == -1)
							PARSE_FAILED(this, false);
						pd += 4;
						sd -= 4;
						tm_year = r - 1900;
						break;
	
					case 'y':
						if ((r = this->ParseNumber(pd, sd, 4)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_year = (r >= 70) ? r : r + 100;
						break;
	
					case 'm':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_mon = r - 1;
						break;
	
					case 'd':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_mday = r;
						break;
	
					case 'H':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_hour = r;
						break;
	
					case 'M':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_min = r;
						break;
	
					case 'S':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_sec = r;
						break;
	
					case '%':
						if (*pd++ != '%')
							PARSE_FAILED(this, false);
						break;
	
					default:
						PARSE_FAILED(this, false);
					}
					state = 0;
				} 	// else
	
				++pf;
				--sf;
			}
	
			return mktime(this) != -1;
		}
	
		bool Time::ParseDate(const Char* date, const Char* format)
		{
			const Char* pd = date;
			const Char* pf = format;
#ifdef _UNICODE
			size_t sd = wcslen(date);
			size_t sf = wcslen(format);
#else
			size_t sd = strlen(date);
			size_t sf = strlen(format);
#endif
			int state = 0;
			memset(this, 0, sizeof(Time));
	
			while (sd > 0 && sf > 0)
			{
				if (state == 0)
				{
					if (*pf == '%')
					{
						state = 1;
					}
					else
					{
						if (*pf != *pd++)
							PARSE_FAILED(this, false);
						--sd;
					}
				}
				else
				{
					int r = 0;
					switch (*pf)
					{
					case 'Y':
						if ((r = this->ParseNumber(pd, sd, 4)) == -1)
							PARSE_FAILED(this, false);
						pd += 4;
						sd -= 4;
						tm_year = r - 1900;
						break;
	
					case 'y':
						if ((r = this->ParseNumber(pd, sd, 4)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_year = (r >= 70) ? r : r + 100;
						break;
	
					case 'm':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_mon = r - 1;
						break;
	
					case 'd':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_mday = r;
						break;
	
					case '%':
						if (*pd++ != '%')
							PARSE_FAILED(this, false);
						break;
	
					default:
						PARSE_FAILED(this, false);
					}
					state = 0;
				} 	// else
	
				++pf;
				--sf;
			}
	
			return mktime(this) != -1;
		}
	
		bool Time::ParseTime(const Char* time, const Char* format)
		{
			const Char* pd = time;
			const Char* pf = format;
#ifdef _UNICODE
			size_t sd = wcslen(time);
			size_t sf = wcslen(format);
#else
			size_t sd = strlen(time);
			size_t sf = strlen(format);
#endif
			int state = 0;
			memset(this, 0, sizeof(Time));
	
			while (sd > 0 && sf > 0)
			{
				if (state == 0)
				{
					if (*pf == '%')
					{
						state = 1;
					}
					else
					{
						if (*pf != *pd++)
							PARSE_FAILED(this, false);
						--sd;
					}
				}
				else
				{
					int r = 0;
					switch (*pf)
					{
					case 'H':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_hour = r;
						break;
	
					case 'M':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_min = r;
						break;
	
					case 'S':
						if ((r = this->ParseNumber(pd, sd, 2)) == -1)
							PARSE_FAILED(this, false);
						pd += 2;
						sd -= 2;
						tm_sec = r;
						break;
	
					case '%':
						if (*pd++ != '%')
							PARSE_FAILED(this, false);
						break;
	
					default:
						PARSE_FAILED(this, false);
					}
					state = 0;
				} 	// else
	
				++pf;
				--sf;
			}
	
			return mktime(this) != -1;
		}
	
		bool Time::operator == (const Time& other) const
		{
			return tm_sec == other.tm_sec &&
			tm_min == other.tm_min &&
			tm_hour == other.tm_hour &&
			tm_mday == other.tm_mday &&
			tm_mon == other.tm_mon &&
			tm_year == other.tm_year &&
			tm_wday == other.tm_wday &&
			tm_yday == other.tm_yday &&
			tm_isdst == other.tm_isdst;
		}
	
		bool Time::operator != (const Time& other) const
		{
			return !(*this == other);
		}
	
		bool Time::operator > (const Time& other) const
		{
			time_t t1 = mktime(const_cast<Time*>(this));
			time_t t2 = mktime(const_cast<Time*>(&other));
			return t1 > t2;
		}
	
		bool Time::operator < (const Time& other) const
		{
			return !(*this > other || *this == other);
		}
	
		bool Time::operator >= (const Time& other) const
		{
			return (*this > other || *this == other);
		}
	
		bool Time::operator <= (const Time& other) const
		{
			return (*this < other || *this == other);
		}
	
		Time& Time::operator = (const Time& other)
		{
			if (this == &other)
			{
				return *this;
			}
			tm_sec = other.tm_sec;
			tm_min = other.tm_min;
			tm_hour = other.tm_hour;
			tm_mday = other.tm_mday;
			tm_mon = other.tm_mon;
			tm_year = other.tm_year;
			tm_wday = other.tm_wday;
			tm_yday = other.tm_yday;
			tm_isdst = other.tm_isdst;
			return *this;
		}
	
		Time& Time::operator = (const tm& other)
		{
			if (this == &other)
			{
				return *this;
			}
			tm_sec = other.tm_sec;
			tm_min = other.tm_min;
			tm_hour = other.tm_hour;
			tm_mday = other.tm_mday;
			tm_mon = other.tm_mon;
			tm_year = other.tm_year;
			tm_wday = other.tm_wday;
			tm_yday = other.tm_yday;
			tm_isdst = other.tm_isdst;
			return *this;
		}
	
		int Time::CompareDay(const Time& other) const
		{
			Time time1 = *this;
			Time time2 = other;
			time1.tm_hour = time1.tm_min = time1.tm_sec = 
				time2.tm_hour = time2.tm_min = time2.tm_sec = 0;
			time_t t1 = mktime(&time1);
			time_t t2 = mktime(&time2);
	
			return static_cast<int>(t1 - t2);
		}
	
		int Time::ParseNumber(const Char* s, size_t len, size_t digits) const
		{
			if (len < digits)
			{
				return -1;
			}
	
			try
			{
				String ss(s, digits);
				return boost::lexical_cast<int>(TO_ASTRING(ss));
			}
			catch (boost::bad_lexical_cast)
			{
				return -1;
			}
		}
	}
}
